using System;
using System.Collections;

using Scope = System.Collections.IDictionary;

namespace Sencha.Serialization.Bson
{
	/// <summary>
	/// Representation of JS Code with Scope in BSON. This type is immutable by design
	/// </summary>
	[TypeSerializer(typeof(Serializers.BsonJavaScriptWithScopeSerializer))]
	public sealed class BsonJavaScriptWithScope : IEquatable<BsonJavaScriptWithScope>
	{
		private static readonly Scope ReadOnlyScope = new ReadOnlyDictionary(new Hashtable());

		public string Code { get; private set; }
		public Scope Scope { get; private set; }

		public BsonJavaScriptWithScope()
			: this(string.Empty, ReadOnlyScope) { }
		public BsonJavaScriptWithScope(string code)
			: this(code, ReadOnlyScope) { }
		public BsonJavaScriptWithScope(string code, Scope scope)
		{
			if (code == null)
				throw new ArgumentNullException("code");
			if (scope == null)
				throw new ArgumentNullException("scope");

			this.Code = code;
			this.Scope = scope;
		}

		#region IEquatable<BsonJavaScriptWithScope> Members

		public override bool Equals(object obj)
		{
			return this.Equals(obj as BsonJavaScriptWithScope);
		}

		public override int GetHashCode()
		{
			return this.Code.GetHashCode();
		}

		public bool Equals(BsonJavaScriptWithScope other)
		{
			if (other == null)
				return false;

			return this.Code.Equals(other.Code) && this.Scope.Equals(other.Scope);			
		}

		#endregion

		public static bool operator ==(BsonJavaScriptWithScope a, BsonJavaScriptWithScope b)
		{
			if (ReferenceEquals(a, null) && ReferenceEquals(b, null))
				return false;
			else if (ReferenceEquals(a, null) || ReferenceEquals(b, null))
				return false;
			else
				return a.Equals(b);

		}
		public static bool operator !=(BsonJavaScriptWithScope a, BsonJavaScriptWithScope b)
		{
			return !(a == b);
		}

		public override string ToString()
		{
			return string.Format("javascript w/scope: {0}, scope: {1}", this.Code != null && this.Code.Length > 20 ? this.Code.Substring(0, 20) + "..." : this.Code, this.Scope);
		}
	}
}